#include "streamrecorder.h"
#include "usersettings.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QRegularExpression>
#include <QStorageInfo>

StreamRecorder::StreamRecorder(const StreamRecorderConfig& config, QObject* parent)
    : QObject(parent), m_config(config)
{
    setupHardwareAcceleration();
}

StreamRecorder::~StreamRecorder()
{
    cleanup();
}

bool StreamRecorder::isRunning() const
{
    return m_active.test();
}

bool StreamRecorder::start()
{
    if (m_active.test_and_set()) {
        qWarning() << "Recorder already active, cannot start again";
        return false;
    }

    // 创建临时目录
    QDir dir { m_config.outputDir };
    if (!dir.exists()) {
        qWarning() << "Output directory does not exist:" << m_config.outputDir.path();
        m_active.clear();
        emit errorOccurred(OUTPUT_DIR_NOT_EXIST_ERROR, tr("Output directory does not exist"));
        return false;
    }

    // 检查空间是否足够
    if (!checkDiskSpace()) {
        m_active.clear();
        emit errorOccurred(INSUFFICIENT_DISK_SPACE_ERROR, tr("Insufficient disk space"));
        return false;
    }

    const QString filename = dir.absoluteFilePath(m_config.outputName);
    m_segmentsDir = QString("%1-segments").arg(QFileInfo(filename).baseName());

    if (dir.exists(m_segmentsDir)) {
        QDir { dir.absoluteFilePath(m_segmentsDir) }.removeRecursively();
    }
    if (!dir.mkdir(m_segmentsDir)) {
        qWarning() << "Failed to create segments directory:" << m_segmentsDir;
        m_active.clear();
        emit errorOccurred(CREATE_SEGMENTS_DIR_ERROR, tr("Failed to create temporary directory"));
        return false;
    }

    // 初始化FFmpeg
    try {
        initFFmpegProcess();
    } catch (const std::exception& e) {
        qWarning() << "Failed to initialize FFmpeg process:" << e.what();
        m_active.clear();
        emit errorOccurred(INIT_RECORD_PROCESS_ERROR, tr("Failed to initialize recording process"));
        return false;
    }

    qDebug() << "StreamRecorder: start" << m_config.inputUrl;

    m_segmentFiles.clear();
    m_processedFrames = 0;
    m_lastValidPosition = 0;
    m_currentFps = 0;
    m_currentBitrate = 0;
    m_streamInfo = StreamInfo {};
    m_buffer.clear();
    m_stopRequested = false;
    m_terminateRequested = false;

    return startConversion();
}

bool StreamRecorder::checkDiskSpace()
{
    QStorageInfo storage(m_config.outputDir.absolutePath());
    if (!storage.isValid() || !storage.isReady()) {
        qWarning() << "Storage is not valid or ready:" << m_config.outputDir.absolutePath();
        return false;
    }

    // 估算需要的空间 (假设平均比特率和录制时长)
    // 假设需要至少5GB空间或者足够存储2小时高清内容
    const qint64 requiredSpace = qint64(5) * 1024 * 1024 * 1024;
    const qint64 availableSpace = storage.bytesAvailable();

    if (availableSpace < requiredSpace) {
        qWarning() << "Insufficient disk space. Available:" << (availableSpace / (1024 * 1024))
                   << "MB, Required:" << (requiredSpace / (1024 * 1024)) << "MB";
        return false;
    }
    return true;
}

void StreamRecorder::stop()
{
    if (m_active.test() && !m_stopRequested) {
        qDebug() << "StreamRecorder: stopping recorder gracefully";
        m_stopRequested = true;
        if (m_process && m_process->state() == QProcess::Running) {
            sendFFmpegCommand("q");
            if (!m_process->waitForFinished(30000)) {
                qWarning() << "FFmpeg process did not stop gracefully, forcing termination";
                m_process->kill();
                emit errorOccurred(TERMINATE_ACTIVELY_ERROR,
                                   tr("Actively terminates the recording task."));
                cleanup();
            }
        } else {
            emit errorOccurred(PROCESS_NOT_RUNNING_ERROR, tr("The FFmpeg process is not running."));
            cleanup();
        }
    }
}

void StreamRecorder::terminate()
{
    if (m_active.test() && !m_terminateRequested) {
        qDebug() << "StreamRecorder: terminating recorder";
        m_terminateRequested = true;
        if (m_process && m_process->state() == QProcess::Running) {
            sendFFmpegCommand("q");
            if (!m_process->waitForFinished(30000)) {
                qWarning() << "FFmpeg process did not terminate in time, killing it";
                m_process->kill();
            }
            emit errorOccurred(TERMINATE_ACTIVELY_ERROR,
                               tr("Actively terminates the recording task."));
        } else {
            emit errorOccurred(PROCESS_NOT_RUNNING_ERROR, tr("The FFmpeg process is not running."));
        }
        // TODO: 删除分片目录
        if (m_config.outputDir.exists(m_segmentsDir)) {
            QDir { m_config.outputDir.absoluteFilePath(m_segmentsDir) }.removeRecursively();
        }
        cleanup();
    }
}

void StreamRecorder::initFFmpegProcess()
{
    Q_ASSERT(!m_process);
    m_process = new QProcess(this);

    // 设置进程环境
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    env.insert("AV_LOG_FORCE_NOCOLOR", "1"); // 禁用颜色输出，更容易解析
#ifdef QT_NO_DEBUG
    const auto now = QDateTime::currentDateTime();
    const auto nd = now.date();
    const auto nt = now.time();
    const auto filename = QString("ffmpeg-%1_%2_%3_%4_%5_%6.txt")
                                  .arg(nd.year())
                                  .arg(nd.month())
                                  .arg(nd.day())
                                  .arg(nt.hour())
                                  .arg(nt.minute())
                                  .arg(nt.second());
    const auto log_path = UserSettings::instance().logDir().absoluteFilePath(filename);
    env.insert("FFREPORT", QString("file='%1':level=32").arg(log_path)); // 添加日志文件用于故障排查
#endif
    m_process->setProcessEnvironment(env);

    // 设置标准输出和标准错误流的读取
    m_process->setProcessChannelMode(QProcess::MergedChannels);

    connect(m_process, &QProcess::readyReadStandardOutput, this, &StreamRecorder::parseOutput);
    connect(m_process, &QProcess::readyReadStandardError, this, &StreamRecorder::parseOutput);
    connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
            &StreamRecorder::handleProcessExit);
    connect(m_process, &QProcess::errorOccurred, this,
            [this](QProcess::ProcessError error) { handleProcessError(error); });

    // initQoSTimers();
    m_lastOutputTime = QDateTime::currentDateTime(); // 初始化最后输出时间
}

void StreamRecorder::initQoSTimers()
{
    m_qosTimer = new QTimer(this);
    m_qosTimer->setInterval(30000);
    connect(m_qosTimer, &QTimer::timeout, this, [this] {
        calculateQoSMetrics();
        adjustBandwidth();
    });
}

void StreamRecorder::setupHardwareAcceleration()
{
    if (m_config.hwAccel) {
#if defined(Q_OS_WIN)
        m_config.customArgs.prepend("-hwaccel");
        m_config.customArgs.prepend("dxva2");
#elif defined(Q_OS_LINUX)
        m_config.customArgs.prepend("-hwaccel");
        m_config.customArgs.prepend("vaapi");
#elif defined(Q_OS_MACOS)
        m_config.customArgs.prepend("-hwaccel");
        m_config.customArgs.prepend("videotoolbox");
#endif
    }
}

bool StreamRecorder::startConversion()
{
    m_buffer.clear();

    // // 检查ffmpeg是否存在
    // QProcess checkProcess;
    // checkProcess.start("ffmpeg", {"-version"});
    // if (!checkProcess.waitForFinished(5000) || checkProcess.exitCode() != 0) {
    //     qWarning() << "FFmpeg not found or not working properly";
    //     emit errorOccurred(1020, tr("FFmpeg not found. Please install FFmpeg."));
    //     cleanup();
    //     return false;
    // }

    // 验证输入URL格式
    if (m_config.inputUrl.trimmed().isEmpty()) {
        qWarning() << "Input URL is empty";
        emit errorOccurred(INVALID_STREAM_URL_ERROR, tr("Input URL cannot be empty"));
        cleanup();
        return false;
    }

    // 构建参数列表前进行安全检查
    QStringList args = { "-y",
                         "-ss",
                         QString::number(m_lastValidPosition / 1000.0),
                         "-i",
                         m_config.inputUrl,
                         "-f",
                         "segment",
                         "-segment_time",
                         QString::number(m_config.segmentDuration),
                         "-reset_timestamps",
                         "1" };

    // 添加超时参数以防止进程卡死
    args << "-timeout" << "60000000"; // 60秒超时(单位:微秒)

    // 添加错误恢复设置
    args << "-reconnect" << "1"
         << "-reconnect_streamed" << "1"
         << "-reconnect_delay_max" << "30"; // 最大延迟30秒

    args << "-movflags" << "+frag_keyframe+empty_moov";

    // 输出文件路径
    args << m_config.outputDir.absoluteFilePath(m_segmentsDir + QString("/output_%03d.mp4"));

    // 保护参数，防止空参数引起问题
    for (const auto& arg : std::as_const(m_config.customArgs)) {
        if (!arg.trimmed().isEmpty()) {
            args << arg.trimmed();
        }
    }

    qDebug() << "StreamRecorder: starting FFmpeg with args:" << args.join(' ');
    m_process->start("ffmpeg", args);

    // 检查进程是否成功启动
    if (!m_process->waitForStarted(10000)) {
        qWarning() << "Failed to start FFmpeg process:" << m_process->errorString();
        emit errorOccurred(START_RECORD_PROCESS_ERROR, tr("Failed to start recording process"));
        cleanup();
        return false;
    }

    // 检查进程是否仍在运行
    if (m_process->state() != QProcess::Running) {
        qWarning() << "FFmpeg process is not running:" << m_process->errorString();
        emit errorOccurred(RECORD_PROCESS_TERMINATED_ERROR,
                           tr("Recording process terminated unexpectedly"));
        cleanup();
        return false;
    }

    m_startTime = QDateTime::currentDateTime();
    if (m_qosTimer) {
        m_qosTimer->start();
    }

    // 启动监控定时器以确保进程保持活动状态
    if (!m_watchdogTimer) {
        m_watchdogTimer = new QTimer(this);
        m_watchdogTimer->setInterval(30000); // 30秒检查一次
        connect(m_watchdogTimer, &QTimer::timeout, this, &StreamRecorder::checkProcessHealth);
    }
    m_watchdogTimer->start();

    return true;
}

void StreamRecorder::checkProcessHealth()
{
    if (!m_process || m_process->state() != QProcess::Running) {
        return; // 进程已经结束，不需要检查
    }

    // 如果超过240000秒没有收到任何输出，可能进程已经卡住
    if (m_lastOutputTime.isValid()
        && m_lastOutputTime.msecsTo(QDateTime::currentDateTime()) > 240000) {
        qWarning() << "FFmpeg process seems to be stuck (no output for 240 seconds)";
        handleError(RECORD_PROCESS_NO_RESPONSE_ERROR, tr("Recording process is not responding"));
    }
}

void StreamRecorder::parseOutput()
{
    m_lastOutputTime = QDateTime::currentDateTime(); // 更新最后输出时间
    m_buffer += m_process->readAllStandardOutput();

    // 如果缓冲区太大，可能表示有问题
    if (m_buffer.size() > 1024 * 1024) { // 超过1MB
        qWarning() << "Output buffer too large, truncating";
        m_buffer = m_buffer.right(512 * 1024); // 保留最后512KB
    }

    while (!m_buffer.isEmpty()) {
        int pos = m_buffer.indexOf('\n', 0);
        if (pos == -1) {
            pos = m_buffer.indexOf('\r', 0);
            if (pos == -1) {
                break;
            }
        }

        // 提取当前行并去除首尾空白字符
        QByteArray line = m_buffer.left(pos).trimmed();
        processLine(line);

        // 移除已处理的部分
        m_buffer.remove(0, (pos + 1));
    }
}

void StreamRecorder::processLine(const QByteArray& line)
{
    // audiochannels
    if (m_streamInfo.audio_channels == 0) {
        int index = line.indexOf("audiochannels   : ");
        if (index == 0) {
            m_streamInfo.audio_channels = line.mid(19).toInt();
            qDebug() << "audiochannels:" << m_streamInfo.audio_channels << line.mid(19);
        }
    }

    // min_bitrate
    if (m_streamInfo.min_bitrate == 0) {
        int index = line.indexOf("min_bitrate     : ");
        if (index == 0) {
            m_streamInfo.min_bitrate = line.mid(19).toInt();
            qDebug() << "min_bitrate:" << m_streamInfo.min_bitrate << line.mid(19);
        }
    }

    // max_bitrate
    if (m_streamInfo.max_bitrate == 0) {
        int index = line.indexOf("max_bitrate     : ");
        if (index == 0) {
            m_streamInfo.max_bitrate = line.mid(19).toInt();
            qDebug() << "max_bitrate:" << m_streamInfo.max_bitrate << line.mid(19);
        }
    }

    // default_bitrate
    if (m_streamInfo.default_bitrate == 0) {
        int index = line.indexOf("default_bitrate : ");
        if (index == 0) {
            m_streamInfo.default_bitrate = line.mid(19).toInt();
            qDebug() << "default_bitrate:" << m_streamInfo.default_bitrate << line.mid(19);
        }
    }

    // 分辨率
    if (m_streamInfo.resolution.isEmpty()) {
        static QRegularExpression reResolution(
                R"(Stream\s+#\d+:\d+:\s*Video:\s*\w+,\s*\w+$\w+,\s*\w+$,\s*(\d{3,4})x(\d{3,4}))");
        QRegularExpressionMatch matchResolution = reResolution.match(line);
        if (matchResolution.hasMatch()) {
            // 提取宽度和高度
            QString width = matchResolution.captured(1);
            QString height = matchResolution.captured(2);
            m_streamInfo.resolution.setWidth(width.toInt());
            m_streamInfo.resolution.setHeight(height.toInt());
            qDebug() << "Extracted resolution:" << width << "x" << height;
        }
    }

    // 进度解析
    static QRegularExpression reProgress(R"(frame=\s*(\d+).*time=(\d+):(\d{2}):(\d{2})\.(\d{2}))");
    QRegularExpressionMatch matchProgress = reProgress.match(line);
    if (matchProgress.hasMatch()) {
        m_processedFrames = matchProgress.captured(1).toInt();
        qint64 ms = matchProgress.captured(2).toInt() * 3600000
                + matchProgress.captured(3).toInt() * 60000
                + matchProgress.captured(4).toInt() * 1000 + matchProgress.captured(5).toInt() * 10;
        m_lastValidPosition = ms;
        Q_EMIT positionChanged(ms);
    }

    // 新分段文件生成
    if (line.startsWith("[segment @")) {
        static QRegularExpression reSegment(R"(Opening\s+'([^']*)'\s+for\s+writing)");
        QRegularExpressionMatch matchSegment = reSegment.match(line);
        if (matchSegment.hasMatch()) {
            const QString path = matchSegment.captured(1);
            const QString filename = QFileInfo(path).fileName();
            m_segmentFiles.append(filename);
            qDebug() << "New segment:" << path << filename << Q_FUNC_INFO;
        }
    }

    // QoS指标
    static QRegularExpression reQos(
            R"(fps\s*=\s*([\d.]+)\s+.*?\s+bitrate\s*=\s*(N/A|[\d.]+)(?:\s*(kbits/s|Kbits/s|Mbits/s|bits/s))?)");
    QRegularExpressionMatch matchQos = reQos.match(line);
    if (matchQos.hasMatch()) {
        m_currentFps = matchQos.captured(1).toDouble();

        QString bitrateStr = matchQos.captured(2);
        QString unit = matchQos.captured(3);

        if (bitrateStr == "N/A") {
            m_currentBitrate = 0; // 或者你可以选择其他默认值
        } else {
            bool ok;
            m_currentBitrate = bitrateStr.toDouble(&ok);
            if (ok && !unit.isEmpty()) {
                // 根据单位转换比特率到bps
                if (unit.contains('k', Qt::CaseInsensitive)) {
                    m_currentBitrate *= 1000;
                } else if (unit.contains('m', Qt::CaseInsensitive)
                           || unit.contains('M', Qt::CaseInsensitive)) {
                    m_currentBitrate *= 1000000;
                }
            } else {
                m_currentBitrate = 0; // 或者你可以选择其他默认值
            }
        }
    }

    // // 检测输入地址错误
    // if (line.contains("Invalid data found when processing input")
    //     || line.contains("Unable to open resource") || line.contains("Connection refused")
    //     || line.contains("No such file or directory") || line.contains("Failed to open input")
    //     || line.contains("HTTP error 404 Not Found")
    //     || line.contains("Server returned 404 Not Found")) {
    //     handleError(CANT_ACCESS_STREAM_SOURCE_ERROR, tr("Invalid input URL or stream source"));
    //     return;
    // }

    // // 更多错误检测
    // if (line.contains("Error while opening encoder") || line.contains("Conversion failed")
    //     || line.contains("Unable to find a suitable output format")
    //     || line.contains("Error initializing output stream")) {
    //     handleErrorOutput(RECORD_PROCESS_ENCODING_ERROR,
    //                       tr("FFmpeg encoding error. See logs for details."));
    //     return;
    // }

    // if (line.contains("Error opening input: End of file")
    //     || line.contains("Error opening input files: End of file")
    //     || line.contains("IO error: End of file")) {
    //     handleErrorOutput(END_OF_FILE_ERROR, tr("End of file. See logs for details."));
    //     return;
    // }
}

double StreamRecorder::calculateAverage(const QList<double>& values) const
{
    return values.isEmpty() ? 0.0
                            : std::accumulate(values.begin(), values.end(), 0.0) / values.size();
}

void StreamRecorder::calculateQoSMetrics()
{
    m_fpsHistory.append(m_currentFps);
    m_bitrateHistory.append(m_currentBitrate);

    if (m_fpsHistory.size() > 5)
        m_fpsHistory.removeFirst();
    if (m_bitrateHistory.size() > 5)
        m_bitrateHistory.removeFirst();

    emit qosMetrics(calculateAverage(m_fpsHistory), calculateAverage(m_bitrateHistory));
}

void StreamRecorder::adjustBandwidth()
{
    const double avgBitrate = calculateAverage(m_bitrateHistory);
    if (avgBitrate > m_config.maxBandwidth * 1.1) {
        int newBitrate = m_config.maxBandwidth * 0.9;
        updateEncodingParams(newBitrate);
    }
}

void StreamRecorder::updateEncodingParams(int newBitrate)
{
    QString cmd = QString("b %1k").arg(newBitrate);
    sendFFmpegCommand(cmd);
}

void StreamRecorder::handleProcessExit(int exitCode, QProcess::ExitStatus exitStatus)
{
    qDebug() << "StreamRecorder: process exit" << exitCode << exitStatus;

    if (exitStatus == QProcess::NormalExit && exitCode == 0) {
        if (!m_terminateRequested) {
            finalizeConversion();
        }
    } else {
        // 检查是否是由于进程刚刚开始就失败（可能是地址错误）
        if (m_processedFrames == 0 && m_lastValidPosition == 0 && !m_stopRequested) {
            handleError(CANT_ACCESS_STREAM_SOURCE_ERROR, tr("Failed to access stream source"));
        } else {
            handleError(RECORD_PROCESS_TERMINATED_ERROR,
                        tr("The recording process terminates unexpectedly."));
        }
    }
}

void StreamRecorder::handleProcessError(QProcess::ProcessError error)
{
    qDebug() << "StreamRecorder: process error" << error;

    switch (error) {
    case QProcess::FailedToStart:
        handleError(START_RECORD_PROCESS_ERROR,
                    tr("FFmpeg failed to start. Please check if FFmpeg is installed correctly."));
        break;
    case QProcess::Crashed:
        handleError(RECORD_PROCESS_CRASHED_ERROR, tr("FFmpeg process crashed unexpectedly"));
        break;
    default:
        qWarning() << tr("FFmpeg process error: %1").arg(m_process->errorString());
        break;
    }
}

void StreamRecorder::finalizeConversion()
{
    qDebug() << "StreamRecorder: begin to merge";

    if (m_segmentFiles.empty()) {
        qWarning() << "No segments to merge";
        emit errorOccurred(NO_SEGMENTS_ERROR, tr("No segments to merge"));
        cleanup();
    } else {
        if (m_merger) {
            disconnect(m_merger, nullptr, this, nullptr);
            m_merger->deleteLater();
            m_merger = nullptr;
        }

        m_merger = new SegmentsMerger(this);
        connect(m_merger, &SegmentsMerger::finished, this, &StreamRecorder::handleMergeFinished);
        connect(m_merger, &SegmentsMerger::errorOccurred, this, &StreamRecorder::handleMergeError);

        const QString segmentsDir = m_config.outputDir.absoluteFilePath(m_segmentsDir);
        const QString outputPath = m_config.outputDir.absoluteFilePath(m_config.outputName);
        if (!m_merger->mergeSegments(segmentsDir, outputPath, m_segmentFiles)) {
            emit errorOccurred(MERGE_FAILED_ERROR, tr("Failed to merge."));
            cleanup();
        }
    }
}

void StreamRecorder::handleMergeFinished(const QString& outputPath)
{
    qDebug() << "StreamRecorder: finishing merging" << outputPath;

    // Delete segments directory after successful merge
    QDir segmentsDir(m_config.outputDir.absoluteFilePath(m_segmentsDir));
    if (segmentsDir.exists()) {
        segmentsDir.removeRecursively();
    }

    Q_EMIT finished(outputPath, m_lastValidPosition);
    cleanup();
}

void StreamRecorder::handleMergeError(int error, const QString& message)
{
    qDebug() << "StreamRecorder: failed to merge" << error << message;

    emit errorOccurred(error, message);
    cleanup();
}

void StreamRecorder::cleanup()
{
    if (m_qosTimer) {
        m_qosTimer->stop();
        m_qosTimer->deleteLater();
        m_qosTimer = nullptr;
    }

    if (m_watchdogTimer) {
        m_watchdogTimer->stop();
        m_watchdogTimer->deleteLater();
        m_watchdogTimer = nullptr;
    }

    if (m_process) {
        disconnect(m_process, nullptr, this, nullptr);
        if (m_process->state() == QProcess::Running || m_process->state() == QProcess::Starting) {
            // First try to terminate gracefully
            m_process->terminate();
            if (!m_process->waitForFinished(10000)) { // Wait 5 seconds for graceful termination
                qWarning() << "Process did not terminate gracefully, forcing kill";
                m_process->kill();
                if (!m_process->waitForFinished(10000)) { // Wait another 5 seconds after kill
                    qWarning() << "Failed to kill process after 10 seconds";
                }
            }
        }
        m_process->deleteLater();
        m_process = nullptr;
    }

    if (m_merger) {
        disconnect(m_merger, nullptr, this, nullptr);
        m_merger->deleteLater();
        m_merger = nullptr;
    }

    // 清除其他状态
    m_buffer.clear();
    m_fpsHistory.clear();
    m_bitrateHistory.clear();
    m_segmentFiles.clear();
    m_processedFrames = 0;
    m_lastValidPosition = 0;
    m_currentFps = 0;
    m_currentBitrate = 0;

    m_active.clear();
    m_stopRequested = false;
    m_terminateRequested = false;

    qDebug() << "StreamRecorder: cleanup complete";
}

void StreamRecorder::sendFFmpegCommand(const QString& cmd)
{
    if (m_process && m_process->state() == QProcess::Running) {
        m_process->write((cmd + "\n").toUtf8());
    }
}

void StreamRecorder::handleError(int error, const QString& message)
{
    qDebug() << "StreamRecorder: handle error" << error << message;

    if (!m_terminateRequested) {
        if (m_segmentFiles.empty()) {
            emit errorOccurred(error, message);
            cleanup();
        } else {
            finalizeConversion();
        }
    }
}
